#include "RWLock.h"

/*
 * Esta es la implementación del read write lock. Usamos dos patrones
 * de semáforos presentados por Allen B. Downey en The Little Book of Semaphores:
 * un Lightswitch para proteger la zona crítica de los lectores y un turnstile o
 * molinete para hacer que los threads pasen de a uno y así evitar la
 * inanición. Gracias a los wrappers y a los patrones encaptulados en las
 * clases, ganamos mucha claridad en el código evitando errores y
 * facilitando la explicación.
 */

/* Lectores */
RWLock::RWLock() {
	/* Implementación del patrón Lightswitch */
	this->llave_de_luz_de_lectura = new Lightswitch();

	/* Representa la zona crítica */
	this->sala_de_lectura_vacia = new Semaphore(1); // Se podría utilizar un Mutex*...

	/*
	 * El molinete se utiliza para que los threads pasen de a uno.
	 * Se comporta como un turnstile para los lectores
	 * y como un mutex para los escritores.
	 */
	this->molinete = new Semaphore(1); // Se podría utilizar un Mutex*...
	 	  	  	  	  	  	  	  	   // ...pero usamos semáforos inicializados en 1.
}

RWLock::~RWLock() {
	/* Se libera la memoria solicitada */
	delete this->molinete;
	delete this->llave_de_luz_de_lectura;
	delete this->sala_de_lectura_vacia;
}

/* Escritores */
void RWLock::rlock() {
	/*
	 * Aquí se realiza el turnstile de los lectores,
	 * de manera que van entrando de a uno a la sala de lectura.
	 */
	this->molinete->wait();
	this->molinete->signal();

	/* El primero en entrar prende la luz de la sala de lectura.*/
	this->llave_de_luz_de_lectura->lock(sala_de_lectura_vacia);

	/* Comienza la Zona Crítica (buen nombre para una serie) */
}

void RWLock::runlock() {
	/*
	 * Después de leer, van saliendo. Pueden salir de a varios.
	 * El último en salir apaga la luz de la sala de lectura.
	 */

	/* Termina la Zona Crítica */

	this->llave_de_luz_de_lectura->unlock(sala_de_lectura_vacia);
}

void RWLock::wlock() {
	/*
	 * Los escritores usan el turnstile como un mutex y también pasan
	 * de a uno, pero podrá haber un solo escritor a la vez en la sala de lectura.
	 */
	this->molinete->wait();

	/* El escritor espera a que la sala esté vacía para poder entrar. */
	this->sala_de_lectura_vacia->wait();

	/* Comienza la Zona Crítica */
}

void RWLock::wunlock() {
	/* Libera en turnstile usado como un mutex cuando termina de escribir. */
	this->molinete->signal();

	/* Termina la Zona Crítica */

	this->sala_de_lectura_vacia->signal(); // Abandona la sala dejándola vacía.
}
